#ifndef SYMBOL_TABLE_H
#define SYMBOL_TABLE_H

#include <map>
#include <string>
#include <vector>
#include <list>

enum etype {AN, AC, SN, SC, TYPE_LESS};

struct contents {
	std::string name; // The name given to the variable in the original file
	int scope; // The scope of the variable
	char type; // n or c for number or character
	char vtype; // a or s for array or scalar
	etype semtype;
	bool active; // true if still active, otherwise false
	std::list<int> params; // The key numbers for the parameters
	std::string funclabel; // The label for the function
	std::string return_address_scalar; // The scalar holding the return address (always) for the function
} ;

class SymbolTable
{
	public:
		SymbolTable(): iNextRegister(1), scope(0), blockNum(0), peak(1) {}

		void RecordImportantVariablePeak();

		// Returns reg number if var is:
		//   -Being declared and not in the symbol table
		//   -Being used and already in the symbol table
		// Otherwise, CheckVar prints an error message to stderr and does exit(1)
		int CheckVar(const char * var, bool isDeclaration, int line_num);

		int CheckFunc(const char * func, bool isDefinition, int line_num, std::list<int> paramlist);

		std::string getLabel(int id);

		std::list<int> getParameters(int id);

		// LookupNew returns true if a newly declared variable is already in the symbol table (bad!).
		//   reg will contain the variable's corresponding register
		// LookupNew returns false if a newly declared variable is not in the symbol table (good)
		//   The name of the register assigned to var will be returned in reg
		bool LookupNew(std::string var, int & reg);

		// LookupOld returns true if a previously declared variable is already in the symbol table (good!).
		//   reg will contain the variable's corresponding register
		// LookupOld returns false if a previously declared variable is not in the symbol table (bad)
		//   reg will be an empty string
		bool LookupOld(std::string var, int & reg);

		// SetType finds all instances of "search" and sets their type to "type" if they are both active
		//   and their type is null
		void SetType(int id, std::string type);

		// make and return a temp scalar variable
		std::string useNextScalar(char type);

		// Make and return a temp array variable
		std::string useNextArray(char type);

		// Claim the next available block number; return its name
		int useNextBlock();

		// IncScope increments the scope
		void IncScope();

		// DecScope decrements the scope and goes through the whole symbol table and deactivates 
		//   all symbols that are now out of scope
		void DecScope();

		void DecFuncScope();

		// Print the entire table
		void PrintTable();

		// Get the enumerated type for an id number
		etype GetEType(int id);

		// Get the type, n or c for an id number
		char GetType(int & id);

		int getScope();

		// Get the variable type, a or s, for an id number
		char GetVType(int id);

		// Get the function's return address scalar
		std::string GetReturn(int id);

		// ValidID returns true if we give it a valid id number and false if we do not
		bool ValidId(int & reg);

		void ClearRegisters();
		int getPeak();

		std::vector<int> getArrayIds();

	private:
		// Claim the next available register for temporary use; return its name
		int useNextRegister();

		std::map<int, contents> symbols;
		int iNextRegister;
		int scope;
		int blockNum;
		int peak;
		int ipeak;
};

#endif

